﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Top.Core;

namespace Top.Redis.Cache
{
    /// <summary>
    /// 缓存Redis实现
    /// Cache存session redis 2号DB
    /// </summary>
    public class CacheByRedis:ICache
    {
        static CacheByRedis()
        {
            RedisProvider.SetConfig("SessionMaster", "SessionSlave");
        }

        /// <summary>
        /// 缓存的实例名称
        /// </summary>
        public string Name
        {
            get
            {
                return "Redis Cache";
            }
        }

        /// <summary> 设置缓存项TODO
        /// </summary>
        /// <param name="key">缓存项名称</param>
        /// <param name="value">缓存值</param>
        /// <param name="slidingExpiredTime">指定时间间隔内不访问缓存即过期</param>
        public void Set(string key, object value, TimeSpan slidingExpiredTime)
        {
            string json = Json.ToJsonString(value);

            RedisProvider.CacheHashSet(key, json, 1, slidingExpiredTime.Ticks);
        }

        /// <summary> 设置缓存项
        /// </summary>
        /// <param name="key">缓存项名称</param>
        /// <param name="value">缓存值</param>
        /// <param name="absoluteExpiredTime">指定时间点过期,无论期间是否访问缓存项</param>
        public void Set(string key, object value, DateTime absoluteExpiredTime)
        {
            string json = Json.ToJsonString(value);
            TimeSpan ts = absoluteExpiredTime-DateTime.Now;

            RedisProvider.CacheHashSet(key, json, 2, 0, ts.Ticks);
        }

        /// <summary> 设置缓存项
        /// </summary>
        /// <param name="key">缓存项名称</param>
        /// <param name="value">缓存值</param>
        /// <param name="slidingExpiredTime">指定时间间隔内不访问缓存即过期</param>
        /// <param name="absoluteExpiredTime">指定时间点过期,无论期间是否访问缓存项</param>
        public void Set(string key, object value, TimeSpan slidingExpiredTime, DateTime absoluteExpiredTime)
        {
            string json = Json.ToJsonString(value);
            TimeSpan ts = absoluteExpiredTime - DateTime.Now;

            RedisProvider.CacheHashSet(key, json, 3, slidingExpiredTime.Ticks, ts.Ticks);
        }

        /// <summary> 新增缓存项,如果key已经存在,返回false
        /// </summary>
        /// <param name="key">缓存项名称</param>
        /// <param name="value">缓存值</param>
        /// <param name="slidingExpiredTime">指定时间间隔内不访问缓存即过期</param>
        public bool Add(string key, object value, TimeSpan slidingExpiredTime)
        {
            bool isExists = RedisProvider.CacheKeyExistSync(key);

            if (isExists)
            {
                return false;
            }

            string json = Json.ToJsonString(value);

            RedisProvider.CacheHashSet(key, json, 1, slidingExpiredTime.Ticks);
            return true;
        }

        /// <summary> 新增缓存项,如果key已经存在,返回false
        /// </summary>
        /// <param name="key">缓存项名称</param>
        /// <param name="value">缓存值</param>
        /// <param name="absoluteExpiredTime">指定时间点过期,无论期间是否访问缓存项</param>
        public bool Add(string key, object value, DateTime absoluteExpiredTime)
        {
            bool isExists = RedisProvider.CacheKeyExistSync(key);

            if (isExists)
            {
                return false;
            }

            string json = Json.ToJsonString(value);
            TimeSpan ts = absoluteExpiredTime - DateTime.Now;

            RedisProvider.CacheHashSet(key, json, 2, 0, ts.Ticks);
            return true;
        }

        /// <summary> 新增缓存项,如果key已经存在,返回false
        /// </summary>
        /// <param name="key">缓存项名称</param>
        /// <param name="value">缓存值</param>
        /// <param name="slidingExpiredTime">指定时间间隔内不访问缓存即过期</param>
        /// <param name="absoluteExpiredTime">指定时间点过期,无论期间是否访问缓存项</param>
        public bool Add(string key, object value, TimeSpan slidingExpiredTime, DateTime absoluteExpiredTime)
        {
            bool isExists = RedisProvider.CacheKeyExistSync(key);

            if (isExists)
            {
                return false;
            }

            string json = Json.ToJsonString(value);
            TimeSpan ts = absoluteExpiredTime - DateTime.Now;

            RedisProvider.CacheHashSet(key, json, 3, slidingExpiredTime.Ticks, ts.Ticks);
            return true;
        }

        /// <summary> 替换缓存值,如果key不存在返回false
        /// </summary>
        /// <param name="key">缓存键</param>
        /// <param name="newValue">新的缓存值</param>
        public bool Replace(string key, object value)
        {
            bool isExists = RedisProvider.CacheKeyExistSync(key);

            if (!isExists)
            {
                return false;
            }

            string json = Json.ToJsonString(value);

            RedisProvider.CacheHashSetField(key, "Key", json);
            return true;
        }

        /// <summary> 替换缓存值,如果key不存在,或oldvalue不正确,返回false
        /// </summary>
        /// <param name="key">缓存键</param>
        /// <param name="oldValue">旧的缓存值</param>
        /// <param name="newValue">新的缓存值</param>
        public bool Replace(string key, object oldValue, object newValue)
        {
            bool isExists = RedisProvider.CacheKeyExistSync(key);

            string value = RedisProvider.CacheHashGetField(key, "Key");

            if (!isExists || (Json.ToJsonString(oldValue) != value))
            {
                return false;
            }
            else
            {
                string json = Json.ToJsonString(newValue);
                RedisProvider.CacheHashSetField(key, "Key", json);
                return true;
            }
        }

        /// <summary> 检查是否包含某个Key的缓存,该操作不应该滑动缓存的过期时间
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <returns></returns>
        public bool ContiansKey(string key)
        {
            return RedisProvider.CacheKeyExistSync(key);
        }

        /// <summary> 检查是否包含某个Key的缓存,并指定是否滑动缓存的过期时间
        /// </summary>
        /// <param name="key">缓存key</param>
        /// <param name="slidingExpiration">是否根据缓存的初始设定,滑动缓存的过期时间</param>
        public bool ContiansKey(string key, bool slidingExpiration)
        {
            if (slidingExpiration)
            {
                SlidingExpireTime(key);
            }

            return RedisProvider.CacheKeyExistSync(key);
        }


        /// <summary> 根据缓存键值获取缓存,并滑动缓存的过期时间,不存在返回null
        /// </summary>
        /// <param name="key">缓存的键</param>
        public object this[string key]
        {
            get
            {
                SlidingExpireTime(key);

                string value = RedisProvider.CacheHashGetField(key, "Key");

                if (!string.IsNullOrEmpty(value))
                {
                    return Json.ToObject(value);
                }
                else
                {
                    return value;
                }
            }
        }

        /// <summary> 根据缓存键值获取缓存,不存在返回null
        /// </summary>
        /// <param name="key">缓存的键</param>
        /// <param name="slidingExpiration">是否根据缓存的初始设定,滑动缓存的过期时间 </param>
        public object this[string key, bool slidingExpiration]
        {
            get
            {
                if (slidingExpiration)
                {
                    SlidingExpireTime(key);
                }

                string value = RedisProvider.CacheHashGetField(key, "Key");

                if (!string.IsNullOrEmpty(value))
                {
                    return Json.ToObject(value);
                }
                else
                {
                    return value;
                }
            }
        }

        /// <summary> 批量获取缓存,并滑动缓存的过期时间
        /// </summary>
        /// <param name="keys">缓存键的集合</param>
        public IDictionary<string, object> GetValues(params string[] keys)
        {
            List<string> ls = RedisProvider.CacheHashGet(keys);

            IDictionary<string, object> dict = new Dictionary<string, object>();

            for (int i = 0; i < keys.Length; i++)
            {
                if (!string.IsNullOrEmpty(ls[i]))
                {
                    dict[keys[i]] = Json.ToObject(ls[i]);
                }
                else
                {
                    dict[keys[i]] = ls[i];
                }
            }

            SlidingExpireTime(keys);

            return dict;
        }

        /// <summary> 批量获取缓存,并指定是否滑动缓存的过期时间
        /// </summary>
        /// <param name="keys">缓存键的集合</param>
        /// <param name="slidingExpiration">是否根据缓存的初始设定,滑动缓存的过期时间 </param>
        public IDictionary<string, object> GetValues(IEnumerable<string> keys, bool slidingExpiration)
        {
            string[] ks = keys.ToArray();
            List<string> ls = RedisProvider.CacheHashGet(ks);

            IDictionary<string, object> dict = new Dictionary<string, object>();

            for (int i = 0; i < ks.Length; i++)
            {
                if (!string.IsNullOrEmpty(ls[i]))
                {
                    dict[ks[i]] = Json.ToObject(ls[i]);
                }
                else
                {
                    dict[ks[i]] = ls[i];
                }
            }

            if (slidingExpiration)
            {
                SlidingExpireTime(ks);
            }

            return dict;
        }

        /// <summary>
        /// 滑动过期时间
        /// </summary>
        /// <param name="key">键</param>
        private void SlidingExpireTime(string key)
        {
            //过期类型0不过期 1滑动 2绝对 3滑动或绝对
            string expireType = RedisProvider.CacheHashGetField(key, "ExpireType");

            if (expireType == "1")
            {
                string expireTime = RedisProvider.CacheHashGetField(key, "SlidingExpiredTime");
                RedisProvider.CacheKeyExpire(key, new TimeSpan(expireTime.To<long>(0)));
            }
            else if (expireType == "3")
            {
                string slidingExpireTime = RedisProvider.CacheHashGetField(key, "SlidingExpiredTime");
                string absoluteExpireTime = RedisProvider.CacheHashGetField(key, "AbsoluteExpiredTime");
                string elapsedExpiredTime = RedisProvider.CacheHashGetField(key, "ElapsedExpiredTime");

                long liveTime = RedisProvider.CacheKeyTTL(key);

                long slidingTime = slidingExpireTime.To<long>(0);
                long absoluteTime = absoluteExpireTime.To<long>(0);
                long elapsedTime = elapsedExpiredTime.To<long>(0);
                
                //已经经过多少时间
                long resultTime = 0;

                if (elapsedTime == 0)
                {
                    resultTime = slidingTime - liveTime;
                }
                else
                {
                    resultTime = elapsedTime + (slidingTime - liveTime);
                }

                RedisProvider.CacheHashSetField(key, "ElapsedExpiredTime", resultTime.ToString());

                if ((resultTime + slidingTime) < absoluteTime)
                {
                    RedisProvider.CacheKeyExpire(key, new TimeSpan(slidingTime));
                }
                else
                {
                    RedisProvider.CacheKeyExpire(key, new TimeSpan(Math.Abs((absoluteTime - resultTime))));
                }
            }
        }

        /// <summary>
        /// 批量滑动过期时间
        /// </summary>
        /// <param name="keys"></param>
        private void SlidingExpireTime(string[] keys)
        {
            for (int i = 0; i < keys.Length; i++)
            {
                SlidingExpireTime(keys[i]);
            }
        }
    }
}
